﻿/* Copyright 2010 Igor Francé
 * 
 * This file is part of ECMADoc (http://ecmadoc.codeplex.com).
 * 
 * ECMADoc is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * 
 * ECMADoc is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with ECMADoc. 
 * If not, see http://www.gnu.org/licenses/.
 */
namespace EcmaDoc.Core.Items
{
	using System;
	using System.Collections.Generic;
	using System.Collections.Specialized;
	using System.Linq;
	using System.Xml;

	/// <summary>
	/// Represents a documention property of a code item.
	/// </summary>
	public class ItemProperty : IParserItem
	{
		internal const string NodeName = "property";

		/// <summary>
		/// Initializes a new instance of the <see cref="ItemProperty"/> class, using the specified property <paramref name="name"/>
		/// and <paramref name="content"/>.
		/// </summary>
		/// <param name="name">The name of the property.</param>
		/// <param name="content">The text content of the property.</param>
		public ItemProperty(string name, string content)
		{
			this.Name = name;
			this.Content = content;
			this.Attributes = new Dictionary<string, string>();
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="ItemProperty"/> class, using the specified property <paramref name="name"/>,
		/// <paramref name="content"/> and <paramref name="attributes"/>.
		/// </summary>
		/// <param name="name">The name of the property.</param>
		/// <param name="content">The text content of the property.</param>
		/// <param name="attributes">The name/value collection of submatches found in and associated with the property to create.</param>
		public ItemProperty(string name, string content, NameValueCollection attributes)
			: this(name, content)
		{
			foreach (string attrib in attributes.Keys)
			{
				if (attrib == name)
					continue;

				if (this.Attributes.ContainsKey(attrib))
					this.Attributes[attrib] = attributes[attrib];
				else
					this.Attributes.Add(attrib, attributes[attrib]);
			}
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="ItemProperty"/> class, using the specified <paramref name="propertyElement"/>.
		/// </summary>
		/// <param name="propertyElement">An XML element previously created for the property to re-create.</param>
		protected ItemProperty(XmlElement propertyElement)
		{
			this.Name = propertyElement.GetAttribute("name");
			this.Content = propertyElement.InnerText;
			this.Attributes = new Dictionary<string, string>();

			foreach (XmlAttribute attrNode in propertyElement.Attributes)
			{
				if (attrNode.Name != "name")
					this.Attributes.Add(attrNode.Name, attrNode.Value);
			}
		}

		/// <summary>
		/// Gets the name of this property.
		/// </summary>
		public string Name
		{
			get;
			private set;
		}

		/// <summary>
		/// Gets the content of this property.
		/// </summary>
		public string Content
		{
			get;
			private set;
		}

		/// <summary>
		/// Gets the attributes of this property.
		/// </summary>
		public Dictionary<string, string> Attributes
		{
			get;
			private set;
		}

		/// <summary>
		/// Factory method, creates a new item property using the XML element previously created for the property to re-create.
		/// </summary>
		/// <param name="propertyElement">An XML element previously created for the property to re-create.</param>
		/// <returns>A new item property using the XML element previously created for the property to re-create.</returns>
		public static ItemProperty Create(XmlElement propertyElement)
		{
			return new ItemProperty(propertyElement);
		}

		/// <summary>
		/// Returns an <see cref="XmlElement"/> that represents the current item.
		/// </summary>
		/// <param name="document">The owner document to use to create the element.</param>
		/// <returns>
		/// An <see cref="XmlElement"/> that represents the current item.
		/// </returns>
		public XmlElement ToXml(XmlDocument document)
		{
			XmlElement self = document.CreateElement(NodeName);
			self.SetAttribute("name", Name);

			foreach (string attrName in Attributes.Keys.Where(t => Attributes[t] != string.Empty))
				self.SetAttribute(attrName, Attributes[attrName]);

			self.InnerText = Content;

			return self;
		}

		/// <summary>
		/// Determines whether the specified <see cref="Object"/> is equal to this instance.
		/// </summary>
		/// <param name="obj">The <see cref="Object"/> to compare with this instance.</param>
		/// <returns>
		/// 	<c>true</c> if the specified <see cref="Object"/> is equal to this instance; otherwise, <c>false</c>.
		/// </returns>
		/// <exception cref="NullReferenceException">
		/// The <paramref name="obj"/> parameter is null.
		/// </exception>
		public override bool Equals(object obj)
		{
			if (ReferenceEquals(null, obj))
				return false;

			if (ReferenceEquals(this, obj))
				return true;

			if (obj.GetType() != typeof(ItemProperty))
				return false;

			return Equals((ItemProperty)obj);
		}

		/// <summary>
		/// Compares this instance with the specified <paramref name="property"/> and returns a value indicating their equality.
		/// </summary>
		/// <param name="property">The property to compare with.</param>
		/// <returns>
		/// <c>true</c> if the two properties are deemed equal; otherwise <c>false</c>.
		/// </returns>
		public bool Equals(ItemProperty property)
		{
			if (ReferenceEquals(null, property))
				return false;
			if (ReferenceEquals(this, property))
				return true;

			bool attribsEqual = false;
			if (this.Attributes.Count == 0 && property.Attributes.Count == 0)
			{
				attribsEqual = true;
			}
			else if (this.Attributes.Count == property.Attributes.Count)
			{
				foreach (string key in this.Attributes.Keys)
				{
					if (!property.Attributes.ContainsKey(key))
						break;

					if (property.Attributes[key] != this.Attributes[key])
						break;
				}

				attribsEqual = true;
			}

			return attribsEqual && property.Name == this.Name && property.Content == this.Content;
		}

		/// <summary>
		/// Returns a hash code for this instance.
		/// </summary>
		/// <returns>
		/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
		/// </returns>
		public override int GetHashCode()
		{
			unchecked
			{
				int result = this.Name != null ? this.Name.GetHashCode() : 0;
				result = (result * 397) ^ (this.Content != null ? this.Content.GetHashCode() : 0);
				result = (result * 397) ^ (this.Attributes != null ? this.Attributes.GetHashCode() : 0);
				return result;
			}
		}
	}
}
